from copy import deepcopy
import turbogears
import cherrypy

from turbogears import error_handler, exception_handler, validate, expose, database, identity, controllers, redirect
from cherrypy._cperror import NotFound
from cherrypy import request
from genshi.template.plugin import ET
import sqlobject

from mnemoply._renewed_lib.widgets import TableForm, Widget, HiddenField, CheckBox, Label, TextArea\
, DataGridWidget,TextReadOnly, TextAreaReadOnly, TextField, GridForm, Button
#from mnemoply._renewed_lib.ma_params import ParamsColumn
from mnemoply._renewed_common_classes import  Wrapper
from mnemoply._renewed_utils import get_by_key_and_delete

from mnemoply._renewed_model.field import AccountFree
from mnemoply.utils import ids2objs

class attrwrapper:
    """Helper class that returns an object's attribute when called.
    This allows to access 'dynamic' attributes (properties) as well as
    simple static ones.
    """
    def __init__(self, name):
        assert isinstance(name, str)
        self.name = name
    def __call__(self, obj):
        return getattr(obj, self.name)

class Entity(dict):                                
    def __getattr__(self, key):
        try: 
             return self[key]  
        except KeyError, k:                         
             raise AttributeError, k 
    
    def __setattr__(self, key, value):              
        self[key] = value                           
 
    def __delattr__(self, key):                      
        try:                                        
            del self[key]
        except KeyError, k: 
            raise AttributeError, k
 
    def __repr__(self):                             
        return self.__class__.__name__ + "(" + dict.__repr__(self) + ")"


class ParamsColumn:
    """Simple struct that describes single DataGrid column.

    Column has:
      - a name, which allows to uniquely identify column in a DataGrid
      - getter, which is used to extract field's value
      - title, which is displayed in the table's header
      - widget, which is used for display
      - params, which is used for create widget
    """

    def __init__(self, name, getter=None, title=None, widget=TextAreaReadOnly, **params):
        if getter and callable(getter):
            self.getter = getter
        else:
            self.getter = lambda(obj): getattr(obj, self.name)# ParamsGrid.attrwrapper(name)
        self.name = name
        self.title = title or name.replace('_',' ').capitalize()
        self.widget = widget
        if widget == HiddenField:
            self.title = ' '
        self.params = params

    def get_value_from_form(self,name,**kw):
        result = None
        if hasattr( self.widget,'get_value_from_form'):
            return self.widget.get_value_from_form(name,**kw)
        else:
            return kw[name] if kw.has_key(name) else None 

    def get_apply_widget(self):
        return ApplyToAllWidget(self.name,self.widget,**self.params)

    def __str__(self):
        return "<Column %s>" % self.name
    
    def get_field_name(self, id):
        return str('%s_%s'%(self.name,id))
    



class Action(controllers.Controller):
    """
        Represent small tab lookup inside other controller.
    """

    columns = []
    title = ""
    name = ''
    show_include = True
    model_object=None
    
    def __init__(self, name = '', title='',columns = None):
        self.name = name or self.name
        self.title = title or self.title or self.name.replace('_',' ').capitalize()
        self.columns = columns or self.columns

        def column_add_check(): 
            left_cols = right_cols = []
            if self.show_include:
                left_cols = [ParamsColumn('include',
                                          getter = lambda(row):  True,
                                          widget=CheckBox
                                          )
                             ] 
            left_cols = left_cols + [ParamsColumn('title')]
            right_cols = [
                ParamsColumn('error',
                             getter = lambda(row):  'xxx',
                             widget = TextField,
                             field_class=['font-red'],
                            # attrs={'style':'color: #ff0000;'},
                             
                            ),
                ParamsColumn('dell',
                             getter = lambda(row):  'xxx',
                             widget = Button,
                             field_class=['font-red'],
                             attrs={'onclick':'removeRow(this);'},
                             
                            )
                ]
            self.columns = left_cols + self.columns + right_cols

            names=[]
            for col in self.columns:
                if col.name in names:
                    raise ValueError('Duplicate column name: %s' % col.name)
                names.append(col.name)

        if self.columns:
            column_add_check()
    

    def apply_one(self, row, obj):
        return True
    
    def check_one(self, row, obj):
        return ''
    
    def row_from_kw(self, id, **kw):
        result = {}
        for col in self.columns:
            name = col.get_field_name(id)
            result[col.name] = kw.get(name)
        return Entity(result)
    
    def check_all(self,rows, objs):
        errors = {}
        return errors
        
    def check(self,ids_list, objs, **kw):
        errors = {}
        rows = []
        for id,obj in zip(ids_list, objs):
            row = self.row_from_kw(id,**kw)
            rows.append(row)
            res = self.check_one(row, objs)
            if res:
                errors['error_%s'%id]=res
        if not errors:
            errors = self.check_all(rows)
        return errors

    def get_widgets(self, objs):
        widget_list = []
        for col in self.columns:
            for obj in objs:
                name = col.get_field_name(obj.id)
                widget = col.widget(name=name, **col.params)
                widget_list.append(widget)
        return widget_list
    
    def get_field_values(self, objs, get_row=None, **kw ):
        if not get_row:
            get_row = lambda(obj):obj
        fields = {}
        for col in self.columns:
            for obj in objs:
                name = col.get_field_name(obj.id)
                default = kw.get(name,'')
                if not default:
                    default = col.getter(get_row(obj))
                fields[name] = default
        return fields
    
       
    def apply(self, ids_list, objs, **kw):
        exceptions = {}
        for id,obj in zip(ids_list, objs):
            row = self.row_from_kw(id,**kw)
            try:
                res = self.apply_one(row, obj)
            except Exception,e:
                exceptions[id] = e.message
        return exceptions

    def set_errors_from_exception (self, exceptions, **kw):
        result = {}
        ids = []
        for id,message in exceptions.iteritems():
            for col in self.columns:
                field = col.get_field_name(id)
                result[field] = kw.get(field)
            result['error_%s'%id]=str(message)
            ids.append(id)
        ids = ','.join(ids)
        result['ids'] = ids
        return result, ids2objs(ids, self.model_object)    

    def save(self, form, objs, ids_list, **kw):
        @validate(form=form)
        def validation(self,  tg_errors=None, **kw):
            return tg_errors, kw
        errors = exceptions = {}
        tg_errors, kw = validation(self, **kw)
        tg_errors=tg_errors or kw.get('tg_errors', None)
        print 'save', ids_list
        if not tg_errors:
            errors = self.check(ids_list, objs, **kw)
            if not errors:
                exceptions = self.apply(ids_list, objs, **kw)
        return tg_errors , errors , exceptions

    def get_dict(self, objs, **kw):
        print 'ggggggggggg'
        column_headers = [col.title for col in self.columns]
        hidden_fields=[HiddenField(name="ids", default=kw.get('ids',''))]
        ww = self.get_widgets(objs)
        result = dict(fields=ww,
                      row_count=len(objs),
                      col_count=len(self.columns),
                      title=self.title,
                      submit_text = _("Apply"),
                      action = self.name,
                      hidden_fields = hidden_fields,
                      column_headers =column_headers,
                      )
        frm = GridForm(**result)
        result['form'] = frm
        result['fields'] = self.get_field_values(objs,**kw)
        return result
    
    @expose(template = "genshi:mnemoply._renewed_templates.lib.ma_param")
    def show_params(self, obj=None, **kw):
        #if not self.params:
        #    self.apply(obj, **kw)
        #frm = TableForm(fields = self.widget_list)

        ids = kw.get('ids','')
        if obj:
            objs = [obj]
            ids = str(obj.id)
        else:
            objs = ids2objs(ids, self.model_object)
        if not objs:
            raise turbogears.redirect('//google.com')
        
        ids_list = ids.split(',')
        result = self.get_dict(objs, **kw)
        if len(kw)>1:
            for i in ids_list:
                kw["error_%s"%str(i)]=''
            kw_old = kw.copy()
            tg_errors , errors , exceptions = self.save(result['form'], objs, ids_list, **kw)
            
            if not (tg_errors or errors or exceptions):
                turbogears.flash('OK')
                raise redirect('..')            
            
            if tg_errors:
                kw_old['tg_errors'] = tg_errors
            if errors:
                kw_old = dict(kw_old,**errors)
            if exceptions:
                kw_old, objs = self.set_errors_from_exception(exceptions, **kw_old)
                result = self.get_dict(objs,**kw_old)
            request.input_values = kw_old
        
        return result
    show_params.expose_object = True

